package nl.weeaboo.sh.objects;

import static nl.weeaboo.sh.level.Level.toTileCoord;

import java.awt.Rectangle;
import java.awt.event.KeyEvent;

import nl.weeaboo.game.gl.GLManager;
import nl.weeaboo.game.input.UserInput;
import nl.weeaboo.sh.ComboCollector;
import nl.weeaboo.sh.Game;
import nl.weeaboo.sh.danmaku.SpellUtil;
import nl.weeaboo.sh.danmaku.UniverseAdapter;
import nl.weeaboo.sh.level.ActiveTiles;
import nl.weeaboo.sh.level.Level;
import nl.weeaboo.sh.level.Room;
import nl.weeaboo.sh.level.Tile;
import nl.weeaboo.sh.model.DamageRect;
import nl.weeaboo.sh.view.CharaImage;
import nl.weeaboo.sh.view.anim.Animation;
import nl.weeaboo.sh.view.anim.AnimationFrame;

public class Player extends Monster {
	
	private static final int   startHp = 100;
	private static final int   startMagic = 100;
	private static final int   knifeMagicCost = 2;
	private static final int   knifePower = 3;
	
	private static final float minWalkSpeed   = 2.50f;
	private static final float maxWalkSpeed   = 8.00f;
	private static final float walkAccel      = 0.20f;
	private static final float attackGroundFriction = 0.10f;
	private static final float airWalkSpeed   = 5.00f;
	private static final float airWalkAccel   = 0.70f;
	//private static final float fastDropSpeed = 12;
	private static final float airKnifeHopSpeed = 3;
	private static final float wallSlideSpeed = 2;
	private static final int   wallSlideTimeThreshold = 8;
	private static final float wallJumpBoostFactor = 1.2f;
	
	private static final float flyAirFriction  = 0.01f;
	private static final float flyAirFriction2 = 0.10f;
	
	private static final int   shootRotations[] = {0, 215, 270, 325, 180, 0, 0, 145, 90, 35};
	private static final float shotSpeed = 30;
	
	private static final float jumpPower = -12;
	private static final float jumpSpeed = -10;
	private static final float terminalVelocity = 20;
	
	private static final int dropPlatformCombo[] = new int[] {KeyEvent.VK_DOWN, KeyEvent.VK_DOWN};
	private static final int dashComboL[] = new int[] {KeyEvent.VK_LEFT, KeyEvent.VK_LEFT};
	private static final int dashComboR[] = new int[] {KeyEvent.VK_RIGHT, KeyEvent.VK_RIGHT};
	
	private Animation anim;
	private Animation a_idle, a_walk, a_tweenRun, a_run, a_wallSlide,
		a_jumpUp, a_jumpDown, a_attackSweep,
		a_attack, a_attackSideUp, a_attackSideDown, a_attackUp, a_attackDown;
	private ComboCollector comboCollector;
	private GhostingHelper ghosting;
	private PlayerCollidable collidable;
	
	private int frame;
	private int lives;
	private int wallDir;
	private int wallSlideTime;
	private int jumps;
	private int jumpTime;
	private int jumpCooldown;
	private int ignorePlatforms;
	private int shootDir;
	private int boostDir;
	private float boostFactor;
	private int boostTime;
	private int attackCooldown;
	private int combo;
	private int comboCooldown;
	private int magic;
	private int maxMagic;
	
	private float deltaX, deltaY;
	
	public Player(Game game, Level level) {
		super(game, level, game.getCharaImage("player.png"));
		//super(game, level, game.getCharaImage("test.png#goomba"));
		
		a_idle = Animation.create(game, "object/sakuya.png", true,
				"idle1", 6, "idle2", 6, "idle3", 6, "idle4", 6);
		a_idle.getLastFrame().setAllowEscape(true);
		a_walk = Animation.create(game, "object/sakuya.png", true,
				"walk1", 6, "walk2", 6, "walk3", 6, "walk4", 6);
		a_walk.getLastFrame().setAllowEscape(true);
		a_tweenRun = Animation.create(game, "object/sakuya.png", false,
				"tweenrun1", 6, "tweenrun2", 6);
		a_run = Animation.create(game, "object/sakuya.png", true,
				"run1", 6, "run2", 6, "run3", 6, "run4", 6);
		a_run.getLastFrame().setAllowEscape(true);
		
		a_wallSlide = Animation.create(game, "object/sakuya.png", true,
				"wallslide", 6);
		a_wallSlide.getLastFrame().setAllowEscape(true);
		a_jumpUp = Animation.create(game, "object/sakuya.png", true,
				"jump_up1", 6, "jump_up2", 6);
		a_jumpUp.getLastFrame().setAllowEscape(true);
		a_jumpDown = Animation.create(game, "object/sakuya.png", true,
				"jump_down1", 6, "jump_down2", 6);
		a_jumpDown.getLastFrame().setAllowEscape(true);
		
		a_attack = Animation.create(game, "object/sakuya.png", false,
				"attack1", 6, "attack2", 6, "attack3", 6);
		a_attack.getLastFrame().setAllowEscape(true);
		a_attackSideUp = Animation.create(game, "object/sakuya.png", false,
				"attack_sideUp1", 6, "attack_sideUp2", 6, "attack_sideUp3", 6);
		a_attackSideUp.getLastFrame().setAllowEscape(true);
		a_attackSideDown = Animation.create(game, "object/sakuya.png", false,
				"attack_sideDown1", 6, "attack_sideDown2", 6, "attack_sideDown3", 6);
		a_attackSideDown.getLastFrame().setAllowEscape(true);
		a_attackUp = Animation.create(game, "object/sakuya.png", false,
				"attack_up1", 6, "attack_up2", 6, "attack_up3", 6);
		a_attackUp.getLastFrame().setAllowEscape(true);
		a_attackDown = Animation.create(game, "object/sakuya.png", false,
				"attack_down1", 6, "attack_down2", 6, "attack_down3", 6);
		a_attackDown.getLastFrame().setAllowEscape(true);

		a_attackSweep = Animation.create(game, "object/sakuya.png", false,
				"attack_sweep1", 6, "attack_sweep2", 6, "attack_sweep3", 6);
		a_attackSweep.getLastFrame().setAllowEscape(true);

		anim = a_idle;
		
		comboCollector = new ComboCollector(new int[] {
			KeyEvent.VK_LEFT, KeyEvent.VK_RIGHT, KeyEvent.VK_UP, KeyEvent.VK_DOWN,
			KeyEvent.VK_Z, KeyEvent.VK_X, KeyEvent.VK_C
		});
		ghosting = new GhostingHelper(10, 2);
		collidable = new PlayerCollidable(game, this);
		
		setMaxHp(startHp);
		maxMagic = startMagic;
		lives = 99;
		reset();
	}

	//Functions
	public void reset() {
		ghosting.reset();
		
		frame = 0;
		setHp(startHp);
		setPower(1f);
		setXY(400, 0);
		setSpeed(0, 0);
		setRotation(0);
		setInvincibilityTime(0);
		
		dir = 1;
		wallDir = 0;
		wallSlideTime = 0;
		jumps = 0;
		jumpTime = 0;
		jumpCooldown = 0;
		ignorePlatforms = 0;
		shootDir = 6;
		boostDir = 0;
		boostFactor = 1;
		boostTime = 0;
		attackCooldown = 0;
		comboCooldown = 0;
		combo = 0;
		magic = startMagic;
	}
		
	public void destroy() {		
		lives--;
		if (!Game.debug && lives < 0) {
			super.destroy();
		} else {
			reset();		
			level.onPlayerDeath();
		}		
	}
	
	public boolean  damage(int d) {
		if (super.damage(d)) {
			invincibilityTime = 60;
			attackCooldown = 0;
			return true;
		}
		return false;
	}
	
	public void update(UserInput input, float dt) {
		deltaX = 0;
		deltaY = 0;
		frame++;
		
		Level level = (Level)game.getLevel();
		Room oldRoom = level.getRoom(Level.toTileCoord(x), Level.toTileCoord(y));
		
		boolean flymode = isFlyMode();
		boolean friction = false;

		if (isInvincible()) invincibilityTime--;
		if (boostTime > 0 && grounded) boostTime--;
		if (stunFlash > 0) stunFlash--;
		if (jumpCooldown > 0) jumpCooldown--;
		if (jumpCooldown < 0) jumpCooldown++;
		if (attackCooldown > 0) attackCooldown--;
		if (comboCooldown > 0) comboCooldown--;
		if (comboCooldown == 0) combo = 0;
		if (grounded) {
			wallSlideTime = 0;
		} else {
			if (wallDir != 0 && !grounded) {
				wallSlideTime++;
			}
		}
		if (flymode) {
			if ((frame & 31) == 0 && magic < maxMagic) magic++;			
		} else {
			if ((frame & 63) == 0 && magic < maxMagic) magic++;			
		}
		comboCollector.update(input, dt);
		
		updateAnimation(input);	
		friction = (!flymode ? updateNormalMode(input) : updateFlyMode(input));
		updateAttacks(input);
		
		//Gravity
		if (flymode) {
			speedY += FLY_GRAVITY_ACCEL;
		} else {
			speedY += GRAVITY_ACCEL;
		}
		
		if (!flymode && isUnderWater()) {
			speedX *= .95f;
			deltaX *= .70f;
			if (!grounded) {
				float f = GRAVITY_ACCEL * .5f;
				speedY -= f;
				deltaY -= f;
			}
		}
		
		//Terminal velocity
		speedX = Math.max(Math.min(speedX, terminalVelocity), -terminalVelocity);
		speedY = Math.max(Math.min(speedY, wallDir == dir ? wallSlideSpeed : terminalVelocity), -terminalVelocity);
		
		deltaX += speedX;
		deltaY += speedY;
		if (deltaY < 0) deltaY = Math.min(-1.0f, deltaY);
		if (deltaY > 0) deltaY = Math.max(1.0f, deltaY);
		
		//Friction
		if (friction) {
			if (flymode) {
				speedX *= 1.0f - flyAirFriction;
				speedY *= 1.0f - flyAirFriction;

				if (Math.abs(speedX) <= .5f) speedX = 0;
				if (Math.abs(speedY) <= .5f) speedY = 0;
			} else if (grounded) {
				speedX *= 1.0f - (attackCooldown == 0 ? groundFriction : attackGroundFriction);
				if (Math.abs(speedX) < 2) {
					speedX = 0;
				}
			}
		}
		
		//Collision Detection
		if (ignorePlatforms > 0) ignorePlatforms--;
		if (grounded && comboCollector.consumeCombo(dropPlatformCombo)) {
			ignorePlatforms = 4;
		}
		
		wallDir = 0;
		boolean wasGrounded = grounded;
					
		int dx = Math.round(deltaX);
		int dy = Math.round(deltaY);
		if (platform != null) {
			dx += platform.getSpeedX();
		}
		
		{ //Limit movement to within room bounds (for closed walls at least)
			int oldX = x, oldY = y;
			x += dx; y += dy;
			limitMovementRoom(oldRoom);
			dx = (dx >= 0 ? Math.min(x-oldX, dx) : Math.max(x-oldX, dx));
			dy = (dy >= 0 ? Math.min(y-oldY, dy) : Math.max(y-oldY, dy));
			x = oldX; y = oldY;			
		}
				
		collide(dx, dy, ignorePlatforms > 0);
		if (!flymode && ignorePlatforms <= 0) {
			handlePlatformCollisions();
		}
		
		//Don't allow jumping immediately when falling off of a platform
		if (!grounded && wasGrounded) {
			jumpCooldown = 10;			
		}
			
		if (flymode) {
			boostTime = 0;
			grounded = false;
			wallDir = 0;
		}
		
		CollisionUtil.collideSprites(level, collidable);
		CollisionUtil.collideTiles(level, collidable);		
		
		limitMovementRoom(oldRoom);
		
		Room room = level.getRoom(Level.toTileCoord(x), Level.toTileCoord(y));
		if (room != oldRoom) {
			level.onPlayerChangeRoom(oldRoom, room);			
		}
		
		ghosting.update(x, y, image, dir == -1);
	}

	public void draw(GLManager glm) {
		if (isFlyMode()) {
			ghosting.draw(glm);
		}
		
		glm.pushColor();
		
		if (isInvincible()) {
			glm.setColor(1, 1, 1, 0.5f + 0.5f * (float)Math.sin(invincibilityTime));
		}

		super.draw(glm);
		
		glm.popColor();
	}
	protected void drawImage(GLManager glm) {
		anim.draw(glm, dir == -1);
	}
	public void drawForeground(GLManager glm) {		
	}
		
	protected void updateAnimation(UserInput input) {
		if (grounded) {
			if (Math.abs(speedX) < minWalkSpeed) {
				if (anim == a_tweenRun || anim == a_run) {
					anim = anim.next(a_idle);
				} else {
					anim.request(a_idle);
				}
			} else if (Math.abs(speedX) < (maxWalkSpeed-minWalkSpeed) / 2) {
				if (anim == a_tweenRun || anim == a_idle) {
					anim = anim.next(a_walk);
				} else {
					anim.request(a_walk);
				}
			} else if (anim == a_idle || anim == a_walk) {
				anim.request(a_tweenRun);
				a_tweenRun.request(a_run);
			} else {
				anim.request(a_run);
			}
		} else {
			if (wallDir == dir && wallSlideTime >= wallSlideTimeThreshold) {
				anim = anim.next(a_wallSlide);
			} else if (speedY <= 0) {
				anim.request(a_jumpUp);
			} else {
				anim.request(a_jumpDown);
			}
		}
		anim = anim.update(1);
		
		AnimationFrame currentFrame = anim.getCurrentFrame();
		if (currentFrame != null) {
			setImage((CharaImage)currentFrame.getImage());
		}		
	}
	protected boolean updateNormalMode(UserInput input) {
		boolean friction = false;		
		if (attackCooldown == 0 && input.isKeyPressed(KeyEvent.VK_LEFT)) {
			if (!grounded) {
				if (!isBoosting()) {
					if (speedX > -airWalkSpeed) {
						speedX = Math.min(-minWalkSpeed, Math.max(speedX - airWalkAccel, -maxWalkSpeed));
					}
					dir = -1;
				}
			} else {
				if (!input.isKeyPressed(KeyEvent.VK_UP) && !input.isKeyPressed(KeyEvent.VK_DOWN)) {
					if (speedX > -maxWalkSpeed) {
						speedX = Math.min(-minWalkSpeed, Math.max(speedX - walkAccel, -maxWalkSpeed));
					}
				} else {
					friction = true;
				}
				dir = -1;
			}			
			if (wallDir != dir) wallDir = 0;
		} else if (attackCooldown == 0 && input.isKeyPressed(KeyEvent.VK_RIGHT)) {
			if (!grounded) {
				if (!isBoosting()) {
					if (speedX < airWalkSpeed) {
						speedX = Math.max(minWalkSpeed, Math.min(speedX + airWalkAccel, maxWalkSpeed));
					}

					dir = 1;
				}
			} else {
				if (!input.isKeyPressed(KeyEvent.VK_UP) && !input.isKeyPressed(KeyEvent.VK_DOWN)) {
					if (speedX < maxWalkSpeed) {
						speedX = Math.max(minWalkSpeed, Math.min(speedX + walkAccel, maxWalkSpeed));
					}
				} else {
					friction = true;
				}
				dir = 1;
			}

			if (wallDir != dir) wallDir = 0;
		} else {
			wallDir = 0;
			friction = true;
		}

		//Boost
		if (grounded) {
			if ((dir == -1 && comboCollector.consumeCombo(dashComboL))
					|| (dir == 1 && comboCollector.consumeCombo(dashComboR)))
			{
				boostDir = dir;
				boostFactor = 0.75f;
				boostTime = 20;
				anim = anim.next(a_run);
			}
		}
		
		//Handle jumps		
		if (input.consumeKey(KeyEvent.VK_X) && jumps < 1 && jumpCooldown == 0) {
			if (!grounded && wallDir == dir && wallSlideTime >= wallSlideTimeThreshold) {
				dir = -dir;
				boostDir = dir;
				boostFactor = wallJumpBoostFactor;
				boostTime = 2;
			} else {
				if (boostTime > 0) boostTime = 2;
				if (!grounded) boostTime = 0;
				
				if (!grounded) jumps++;
				if (platform != null) {
					speedX += platform.getSpeedX();
				}
			}
			
			jumpTime = 10;
			anim = anim.next(a_jumpUp);
			//game.playSound("jump.wav");
		}		
		if (jumpTime > 0) {
			jumpTime--;
			if (jumpTime > 0 && input.isKeyDown(KeyEvent.VK_X)) {
				deltaY = jumpPower;
				speedY = -GRAVITY_ACCEL;
			} else {
				speedY = jumpSpeed;
				jumpTime = 0;
			}
		}
		if (isBoosting()) {
			deltaX = boostDir * maxWalkSpeed * boostFactor;
		}		
		
		return friction;
	}
	
	protected boolean updateFlyMode(UserInput input) {
		float maxSpd = airWalkSpeed;
		if (input.isKeyDown(KeyEvent.VK_X)) {
			maxSpd = maxWalkSpeed;
		}
		
		if (attackCooldown == 0) {
			boolean h = false;
			boolean v = false;
			
			if (input.isKeyPressed(KeyEvent.VK_LEFT)) {
				if (speedX > -maxSpd) {
					speedX = Math.min(-minWalkSpeed, Math.max(speedX - walkAccel, -maxSpd));
				}
				dir = -1;
				h = true;
			} else if (input.isKeyPressed(KeyEvent.VK_RIGHT)) {
				if (speedX < maxSpd) {
					speedX = Math.max(minWalkSpeed, Math.min(speedX + walkAccel, maxSpd));
				}
				dir = 1;
				h = true;
			}
			
			if (input.isKeyPressed(KeyEvent.VK_UP)) {
				if (speedY > -maxSpd) {
					speedY = Math.min(-minWalkSpeed, Math.max(speedY - walkAccel - FLY_GRAVITY_ACCEL, -maxSpd));
				}
				v = true;
			} else if (input.isKeyPressed(KeyEvent.VK_DOWN)) {
				if (speedY < maxSpd) {
					speedY = Math.max(minWalkSpeed, Math.min(speedY + walkAccel, maxSpd));
				}
				v = true;
			}
			
			if (h || v) {
				if (!h) speedX *= (1.0f - flyAirFriction2);
				if (!v) speedY *= (1.0f - flyAirFriction2);
			}
		}
				
		return true;
	}
	
	protected void updateAttacks(UserInput input) {
		boolean flymode = isFlyMode();
		
		boolean left = input.isKeyPressed(KeyEvent.VK_LEFT);
		boolean right = input.isKeyPressed(KeyEvent.VK_RIGHT);
		boolean throwKeyPressed = input.consumeKey(KeyEvent.VK_Z);
		boolean attackKeyPressed = input.consumeKey(KeyEvent.VK_C);
		
		if (!grounded && wallDir == dir) { //Wallhugging
			boolean t = left;
			left = right;
			right = t;
		}
		
		if (left) {
			if (input.isKeyPressed(KeyEvent.VK_UP)) shootDir = 7;
			else if (input.isKeyPressed(KeyEvent.VK_DOWN)) shootDir = 1;
			else shootDir = 4;
		} else if (right) {
			if (input.isKeyPressed(KeyEvent.VK_UP)) shootDir = 9;
			else if (input.isKeyPressed(KeyEvent.VK_DOWN)) shootDir = 3;
			else shootDir = 6;
		} else {
			if (input.isKeyPressed(KeyEvent.VK_UP)) shootDir = 8;
			else if (input.isKeyPressed(KeyEvent.VK_DOWN)) shootDir = 2;
			else shootDir = (dir < 0 ? 4 : 6);
		}
		
		if (throwKeyPressed && !attackKeyPressed && useMagic(knifeMagicCost)) {
			game.getSoundManager().playSound("throw_knife.wav", 1);

			int ix = x;
			int iy = y - getLevelHitBox().height/2;
			
			for (int n = 0; n < 3; n++) {
				Shot shot = new Shot(game, level, this, game.getImage("knife.png", true), 8);
				shot.setPower(knifePower);
				shot.setXY(ix, iy);
				shot.setSpeed(shotSpeed);
				shot.setRotation(shootRotations[shootDir]);
				shot.advance(shotSpeed/2, 16*(n-1));
				
				level.addSprite(shot);
			}
			
			if (!flymode) {
				if (!grounded && shootDir >= 1 && shootDir <= 3) {
					speedY = Math.min(speedY, airKnifeHopSpeed);
				}
			} else {
				speedX = Math.max(-airKnifeHopSpeed/8, Math.min(speedX, airKnifeHopSpeed/8));
				speedY = 0;
			}
			
			//TODO: Change animation to knife-throw anim. 
		}
		
		//Regular attack
		if (attackKeyPressed) {
			if (attackCooldown <= 0) {
				if (combo == 0 || combo == 2) {
					game.getSoundManager().playSound("slash_med.wav", 1);
				} else {
					game.getSoundManager().playSound("slash_fast.wav", 1);
				}

				jumpCooldown = 12;
				attackCooldown = (flymode ? 6 : jumpCooldown);
				comboCooldown = 30;
				combo++;
				
				if (grounded && input.isKeyDown(KeyEvent.VK_Z)) {
					/*
					System.err.println("POWER HIT");
					
					Explosion e = new Explosion(game, level,
							game.getCharaImage("object/sakuya-fx.png#flash_down"),
							x, y, 0, false, true);
					level.addEffect(e);
					*/
				}

				if (shootDir == 7 || shootDir == 9) {
					anim = anim.next(a_attackSideUp);
				} else if (shootDir == 8) {
					anim = anim.next(a_attackUp);
				} else if (shootDir == 1 || shootDir == 3) {
					if (grounded) anim = anim.next(a_attackSweep);
					else anim = anim.next(a_attackSideDown);
				} else if (shootDir == 2) {
					if (grounded) anim = anim.next(a_attackSweep);
					else anim = anim.next(a_attackDown);
				} else {
					anim = anim.next(a_attack);
				}
			}
		}		
	}
	
	protected void handlePlatformCollisions() {
		Platform oldPlatform = platform;
		platform = null;
		
		int pad = 4;
		
		Rectangle levelHitBox = getLevelHitBox();
		Rectangle a = new Rectangle(
				x+levelHitBox.x,
				y+levelHitBox.y,
				levelHitBox.width,
				levelHitBox.height + pad);
		Rectangle b = new Rectangle();
		for (Sprite sprite : level.getSprites()) {
			if (sprite instanceof Platform) {
				Platform s = (Platform)sprite;
				for (Rectangle r2 : s.getHitRects()) {
					b.x = s.getX() + r2.x;
					b.y = s.getY() + r2.y;
					b.width = r2.width;
					b.height = r2.height;
					
					if (a.intersects(b)) {
						a.height -= pad;
						
						if (deltaY >= 0) {							
							int z = b.y+y-a.y-a.height;

							if (z <= y && y-deltaY+Math.min(0, s.getSpeedY()) <= z) {
								y = z;
								if (deltaY > 0 && oldPlatform != s) {
									onLand();
									speedX = 0;
								}
								grounded = true;
								speedY = s.getSpeedY();
								platform = s;
							}
						}
					}
				}
			}
		}		
	}
	
	public void onHitConnect(Sprite damaged, Rectangle hitArea, DamageRect dr) {
		super.onHitConnect(damaged, hitArea, dr);
		
		if (isFlyMode()) {
			float f = (Math.abs(dr.knockbackX) + Math.abs(dr.knockbackY)) / 4f;
			if (f > 0.001f) {
				float a = SpellUtil.angleTo(x, y, hitArea.x+hitArea.width/2, hitArea.y+hitArea.height/2);
				setSpeed(f * SpellUtil.fastCos(a), f * SpellUtil.fastSin(a));
			}
			
			anim = (speedY <= 0 ? a_jumpUp : a_jumpDown);
			setAttackCooldown(20);
		}		
	}
	protected void onHitWall(int dir, boolean wallJumpable) {
		super.onHitWall(dir, wallJumpable);
		
		if (wallJumpable) {
			wallDir = dir;
		}
		
		speedX = 0;
		jumps = 0;
		boostFactor = 1.0f;
		boostTime = 0;
	}
	protected void onHitCeiling() {
		super.onHitCeiling();
		
		speedY = 0;
		jumpTime = 0;
	}
	protected void onLand() {
		super.onLand();
		
		//boostTime = 0;
		jumps = 0;
	}
	protected boolean useMagic(int m) {
		if (magic >= m) {
			setMagic(magic - m);
			return true;
		}
		return false;
	}
	
	//Getters
	public int getLives() { return lives; }
	public int getMaxHp() { return maxHp; }
	public int getMagic() { return magic; }
	public int getMaxMagic() { return maxMagic; }
	protected boolean isBoosting() { return boostTime > 0; }
	protected boolean isFlyMode() {
		UniverseAdapter ua = game.getLevel().getUniverseAdapter();
		return ua != null;// && ua.getBounds().contains(x, y);
	}
	protected boolean isUnderWater() {
		ActiveTiles at = level.getActiveTiles();
		Rectangle lhb = getLevelHitBox();
		int atx = at.getX();
		int aty = at.getY();
		
		Tile tile = at.getTile(toTileCoord(x+lhb.x+(lhb.width>>1))-atx,
				toTileCoord(y+lhb.y+(lhb.height>>1))-aty);
		
		if (tile != null && tile.isWater()) {
			return true;
		}
		return false;
	}
	public ComboCollector getComboCollector() { return comboCollector; }
	
	//Setters
	public void setAttackCooldown(int c) { attackCooldown = c; }
	public void setHp(int h) { super.setHp(Math.min(maxHp, h)); }
	public void setMagic(int m) { magic = Math.max(0, Math.min(maxMagic, m)); }
	
}
